
/***************************************************************************
 *   Copyright (C) 1997 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "wimp.h"
#include "wimpt.h"
#include "werr.h"
#include "dbox.h"
#include "menu.h"
#include "event.h"
#include "os.h"
#include "flex.h"
#include "bbc.h"

#include "narc.h"
#include "hdrs.h"
#include "regexmod.h"


extern char *path_choices;
extern OPTIONS options;
extern int transport_opts;
extern FOLDREC src_fr;
extern FOLDREC cty_fr;
extern FOLDREC list_fr[N_LISTS];
extern CARDFILE_REC cardfile[N_CARDFILES];
extern int dbox_menu_field;
extern wimp_menustr *wmenu_boxes_names;
extern int lists_line_height;
extern int standard_font;
extern int status_sprite_offset;
extern char fname_temp2[];
extern int import_filters;

extern int regex_err;
extern NEWSG **ng_index;
extern int  n_ngroups;
extern int fetch_list_used;
extern int fetch_count;

extern folder_base;   /* address book section */

/******************************************************************/
/*               MAILING LIST SERVER                              */
/******************************************************************/



int distlist_manage(char *list_name, char *addr_in, int add, char *text)
/**********************************************************************/
{
	FILE *f;
	FILE *f_out;
	int  length;
	int  count;
	char *p;
	char *addr;
	char *name;
	char *temp_fname;
	char addr_buf[128];
	char path[200];
	char buf[200];

	strcpy(addr_buf,addr_in);
	name = addr = addr_buf;

	addr = reply_extract_email_addr(addr,1);
	if(addr == NULL)
		return(1);

	if((p = strchr(addr,'@')) == NULL)
		return(2);

	if(strchr(p,'.') == NULL)
		return(2);

	if((addr-1) > name)
		addr[-2] = 0;

	length = strlen(addr);

	/* look for the email address in the distribution list */
	sprintf(path,"%s.Maillists.%s",path_choices,list_name);
	f = fopen_werr(path,"r",NULL);
	if(f == NULL)
		return(3);

	temp_fname = get_temp_fname();
	f_out = fopen(temp_fname,"w");
	if(f_out == NULL)
	{
		fclose(f);
		return(3);
	}

	while(!feof(f))
	{
		if(fgets(buf,sizeof(buf),f) == NULL)
			break;

		if(memcmp_lc2(buf,addr,length)==0)
		{
			/* delete the address from the list, skip over it */
		}
		else
		{
			fputs(buf,f_out);
		}
	}
	if(add==1)
	{
		if(name[0] == 0)
			name = text;
		get_today_date2(buf);
		fprintf(f_out,"%s",addr);

		count = strlen(addr);
		while(count < 24)
		{
			fputc('\t',f_out);
			count += 8;
		}
		fprintf(f_out,"\t%s",name);

		count = strlen(name);
		if(strlen(addr) >= 32)
			count+=8;

		while(count < 16)
		{
			fputc('\t',f_out);
			count += 8;
		}
		fprintf(f_out,"\t%s\n",buf);
	}

	fclose(f);
	fclose(f_out);

	remove(path);
	file_move(temp_fname,path);
	return(0);
}   /* end of distlist_manage */




int mail_list_check_sender(char *maillist_name, char* author)
/***********************************************************/
{
	char *addr;
	FILE *f;
	int  length;
	char path[128];
	char buf[200];

	addr = reply_extract_email_addr(author,3);
	if(addr == NULL)
		return(-2);
	length = strlen(addr);

	sprintf(path,"%s.Maillists.%s",path_choices,maillist_name);
	f = fopen(path,"r");
	if(f == NULL)
		return(-1);

	while(!feof(f))
	{
		if(fgets(buf,sizeof(buf),f) == NULL)
			break;

		if(memcmp_lc2(buf,addr,length)==0)
		{
			fclose(f);
			return(0);  /* Found OK */
		}
	}
	fclose(f);

	return(-1);    /* not found */
}   /* end of mail_list_check_sender */




void mail_list_server(CARD_EXPANDED *cardex, int user)
/****************************************************/
{
	int  len;
	MNEM_TAB *m;
	char *maillist_name;
	int  mlist_type;
	OPTIONS_MAILBOX *mbox;
	char buf[80];

	static MNEM_TAB commands[] = {
		"signon",1,
		"signoff",2,
		"subscribe",1,
		"suscribe",1,
		"subcribe",1,
		"unsubscribe",2,
		"unsuscribe",2,
		"unsubcribe",2,
		/* "send",2, */
		NULL,-1
	};

	mbox = &options.mailbox[user];
	maillist_name = get_user_id(mbox);

	/* look for mail list commands, but not it it's a Private mailing list */
	if(mbox->mail_list_server != 3)
	{
		m = &commands[0];
		while(m->mnem != NULL)
		{
			sprintf(buf,"%s",m->mnem);
			len = strlen(buf);
			if(memcmp_lc(cardex->title,buf,len)==0)
			{
				if(distlist_manage(maillist_name,cardex->author,m->value,&cardex->title[len])==0)
				{
					reply_receipt_ack(cardex,cardex->author,user,m->value);
				}
				return;
			}
			m++;
		}
	}

	mlist_type = mbox->mail_list_server;
	if((mlist_type == 2) || (mlist_type == 3) || (mlist_type == 4))
	{
		/* Automatic or Private response */
		if((mlist_type==4) && (mail_list_check_sender(maillist_name,cardex->author) < 0))
		{
			/* the sender is not a member of the mailing list */
			reply_receipt_ack(cardex,cardex->author,user,3);
		}
		else if(reply_destinations(maillist_name,&options.mailbox[user],0x10)==3)
		{
			werr(0,"Distribution list not found for Mail List '%s'",maillist_name);
		}
		else
		{
			bounce_article(cardex->text_anchor,cardex->text_start,cardex->text_length,user,4,-1);
			remove(fname_temp2);

			/* set the "fetched" indicator to indicate that a reply has been sent */
			cardex->replied = 1;
		}
	}

}   /* end of mail_list_server */






/******************************************************************/
/*               KILLFILE                                         */
/******************************************************************/

extern BLIST killlist;
extern int  n_killfile;
extern KILLFILE **killfile;
extern int *killfile_regex;




void killfile_free_regex()
/************************/
/* Free any regex handles */
{
	int  ix;
	os_error *error;

	if(killfile_regex)
	{
		for(ix=0; ix<(n_killfile*2); ix++)
		{
			if(killfile_regex[ix] != 0)
			{
				error = os_swi1(REGX_Free+os_X,killfile_regex[ix]);
			}
		}
	}
}   /* end of killfile_free_regex */





void keywords_add(CARD_EXPANDED *cardex, char *keys)
/**************************************************/
/* Add to list of keywords */
{
	int len;

	len = strlen(cardex->keywords);
	if(len >= (sizeof(cardex->keywords)-5))
		return;

	if((len > 0) && (cardex->keywords[len-1] != ' '))
		cardex->keywords[len] = ' ';

	strncpy(&cardex->keywords[len+1],keys,sizeof(cardex->keywords)-len-1);
	cardex->keywords[sizeof(cardex->keywords)-1] = 0;
}   /* end of keywords_add */



static char *addr_start;
static char *addr_domain;




static int article_killfile2(CARD_EXPANDED *cardex,char **text,
							 int type,char *string,int length,
							 ARTICLE_IN *art_in, int regex, int case_sens)
/***********************************************************************************************/
{
	char *p;
	int  c;
	int  i;
	char *p2;
	int  count;
	int  len;
	char *p1;
	char *p_end;
	int  found = -1;

	if(regex_err)
		regex = 0;

	switch(type)
	{
	case KF_AUTHOR:
		if(addr_start != NULL)
		{
			if(regex)
			{
				p = cardex->author;
				if(search_regex(string,length,&p,0,-1,0,regex,NULL)>=0)
					found = KF_AUTHOR;
			}
			else if(string[0] == '<')
			{
				if(memcmp_lc(addr_start,string,length)==0)
					found = KF_AUTHOR;
			}
			else
			{
				if(memcmp_lc(&addr_start[1],string,length)==0)
					found = KF_AUTHOR;
			}
		}
		break;

	case KF_DOMAIN:
		if(addr_domain != NULL)
		{
			if(memcmp_lc(addr_domain,string,length)==0)
				found = KF_DOMAIN;
		}
		break;

	case KF_TITLE:
		if(memcmp_lc(cardex->title,string,length)==0)
			found = KF_TITLE;
		break;

	case KF_TITLE_CONTAINS:
		p = cardex->title;
		if(search_regex(string,length,&p,0,-1,0,regex,NULL) >= 0)
			found = KF_TITLE;
		break;

	case KF_MESSAGE_ID:
	case KF_MESSAGE_ID2:
		if(art_in->references < 0)
			break;

		p = *text + art_in->references;
		p2 = *text + art_in->references_end;
		c = string[0];

		if(regex)
		{
			if(search_regex(string,length,text,art_in->references,art_in->references_end-art_in->references,0,regex,NULL)>=0)
				found = KF_MESSAGE_ID;
			break;
		}

		while(p < p2)
		{
			if(*p == c)
			{
				if(memcmp(p,string,length)==0)
				{
					found = KF_MESSAGE_ID;
					break;
				}
			}
			p++;
		}
		break;


	case KF_PATH:
		/* match the end of the Path: string */
		if(art_in->path < 0)
			break;

		if(regex)
		{
			if(search_regex(string,length,text,art_in->path,art_in->path_end-art_in->path,0,regex,NULL)>=0)
				found = KF_PATH;
			break;
		}

		p = *text + art_in->path_end;

		if(memcmp_lc(p - length, string, length)==0)
			found = KF_PATH;
		break;


	case KF_NEWSGROUP:
		/* is the specified newsgroup present ? */
		if(art_in->newsgroups < 0)
			break;

		p = *text + art_in->newsgroups;

		if(regex)
		{
			if(search_regex(string,length,text,art_in->newsgroups,art_in->newsgroups_end-art_in->newsgroups,0,regex,NULL)>=0)
				found = KF_NEWSGROUP;
			break;
		}

		for(;;)
		{
			if(memcmp_lc(p,string,length)==0)
			{
				found = KF_NEWSGROUP;
				break;
			}

			while(((c = *p++) != ',') && (c != '\n'));
			if(c == '\n')
				break;
		}
		break;

	case KF_SOURCE:
		if(cardex->source == string[0])
		{
			found = KF_SOURCE;
		}
		break;

	case KF_HEADER:
		if(art_in->header_end > 0)
		{
			if(search_regex(string,length,text,0,art_in->header_end,0,regex,NULL) >= 0)
				found = KF_HEADER;
		}
		break;

	case KF_TEXT:
		/* look for sig separator or attachment to end the searchable text */
		p = p1 =  *text + art_in->header_end;
		p_end = *text + art_in->text_length - 6;

		len = art_in->text_length - art_in->header_end;

		p_end -= 6;

		while(p < p_end)
		{
			if(p[0] == '\n')
			{
				if((p[1] == '-') && (memcmp(&p[2],"- \n",3)==0))
				{
					len = p - p1;
					break;
				}
				if((p[1] == 'b') && (memcmp(&p[2],"egin ",5) &&
									 isdigit(p[7]) && isdigit(p[8]) && isdigit(p[9])))
				{
					len = p - p1;
					break;
				}
			}
			p++;
		}

		if(search_regex(string,length,text,
						art_in->header_end,len,0,regex,NULL) >= 0)
			found = KF_TEXT;
		break;


	case KF_ENVELOPE_TO:
		if(art_in->envelope_to >= 0)
		{
			if(search_regex(string,length,text,art_in->envelope_to,art_in->envelope_to_len,0,regex,NULL)>=0)
				found = KF_ENVELOPE_TO;
		}
		else
		{
			// no Envelope-to: in this message header, look at To: and Cc: instead
			if(art_in->to >= 0)
			{
				if(search_regex(string,length,text,art_in->to,art_in->to_len,0,regex,NULL)>=0)
					found = KF_ENVELOPE_TO;
			}
			if((found == -1) && (art_in->cc >= 0))
			{
				if(search_regex(string,length,text,art_in->cc,art_in->cc_len,0,regex,NULL)>=0)
					found = KF_ENVELOPE_TO;
			}
		}
		break;

	case KF_CONTENT_TYPE:
		if(art_in->content_type >= 0)
		{
			if(search_regex(string,length,text,art_in->content_type,art_in->content_type_len,0,regex,NULL)>=0)
				found = KF_CONTENT_TYPE;
		}
		break;

	case KF_CAPS:
		p = cardex->title;
		if(*p == 0) break;

		while((c = *p++) != 0)
		{
			if(islower(c))
				break;
		}
		if(c == 0)
			found = KF_CAPS;
		break;

	case KF_NUM_GROUPS:
		/* count number of newsgroups */
		if(art_in->newsgroups > 0)
		{
			count=1;
			p = *text;
			for(i=art_in->newsgroups; i<art_in->newsgroups_end; i++)
			{
				if(p[i]==',')
					count++;
			}
			if((i = atoi(string)) > 0)
			{
				if(count > atoi(string))
					found = KF_NUM_GROUPS;
			}
		}
		break;

	case KF_ADDR_BOOK:    /* is sender in the address book ? */
		if(art_in->addr_book_entry != NULL)
		{
			// if (art_in->addr_book_entry->folder == string[0])
			found = KF_ADDR_BOOK;
		}
		break;
	}

	return(found);
}   /* end of article_killfile2 */





int article_killfile(CARD_EXPANDED *cardex, char **text, int action, ARTICLE_IN *art_in)
/**************************************************************************************/
/* Is the Box determined from the contents of the article ?
   type:  bit 0 news, bit 1 mail, bit 2 maillist

   Return 1=box has been changed */
{
	int  ix;
	char *p;
	int  c;
	KILLFILE *k;
	int  found;
	int  any_found = 0;
	int  found2;
	int  quoting;
	int  type;
	int  set_box=255;
	int  set_copy=255;
	int  set_source=0;
	int  set_category=0;
	int  set_colour=0;
	int  set_status=0;
	int  score;

	/* which types are fast, if they appear as 2nd condition, do it first */
	static fast_types[20] = {0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0};

	static char status_values[5] = {0,STATUS_UNREAD,STATUS_READ,STATUS_LOCKED,STATUS_MARKED};

	if(n_killfile == 0)
		return(0);

	if(action & 0x10000)
		return(0);   /* don't filter Draft articles */

	addr_start = NULL;
	addr_domain = NULL;

	type = (action >> 6) & 3;

	if(import_filters != 0)
		type = import_filters;

	if(action & 0x10)
	{
		type = 16;  /* outgoing message */
	}
	else if(type == 0)
	{
		type = 8;   /* text,not news or mail */
	}
	else
	{
		if((type == 2) && (cardex->status_other2 & STATUS_BIT_MAILLIST))
			type = 4;
	}

	cardex->score = 0;

	/* find email address */
	p = cardex->author;
	quoting = 0;
	while((c = *p) != 0)
	{
		if(c=='"')
			quoting ^= 1;

		if(quoting==0)
		{
			if(c == '<')
				addr_start = p;
			else if(c == '@')
			{
				addr_domain = p;
			}
		}
		p++;
	}
	if(addr_start == NULL)
		addr_start = cardex->author;

	for(ix=0; ix<n_killfile; ix++)
	{
		found = -1;
		k = killfile[ix];

		if(k->active == 0)
			continue;

		if((type & k->flags) == 0)
			continue;


		if(fast_types[k->type2])
		{
			/* do second condition KF_SOURCE first because it's more efficient */
			p = &k->string[k->string2];
			found = article_killfile2(cardex,text,k->type2,p,strlen(p),art_in,killfile_regex[ix*2+1],k->regex >> 4);

			if(k->regex & KF_OR)
			{
				if(found == -1)
					found = article_killfile2(cardex,text,k->type,k->string,k->length,art_in,killfile_regex[ix*2],k->regex >> 2);
			}
			else if(k->regex & KF_AND_NOT)
			{
				if(found == -1)
					found = article_killfile2(cardex,text,k->type,k->string,k->length,art_in,killfile_regex[ix*2],k->regex >> 2);
				else
					found = -1;
			}
			else if(found >= 0)
			{
				/* must be AND */
				found = article_killfile2(cardex,text,k->type,k->string,k->length,art_in,killfile_regex[ix*2],k->regex >> 2);
			}
		}
		else
		{
			found = article_killfile2(cardex,text,k->type,k->string,k->length,art_in,killfile_regex[ix*2],k->regex >> 2);
			if(k->type2 != 0)
			{
				p = &k->string[k->string2];

				if(k->regex & KF_OR)
				{
					if(found == -1)
					{
						found = article_killfile2(cardex,text,k->type2,p,strlen(p),art_in,killfile_regex[ix*2+1],k->regex >> 4);
					}
				}
				else if(found >= 0)
				{
					/* match on second condition if there is one */
					found2 = article_killfile2(cardex,text,k->type2,p,strlen(p),art_in,killfile_regex[ix*2+1],k->regex >> 4);

					if(k->regex & KF_AND_NOT)
					{
						if(found2 >= 0) found = -1;  /* A and not B */
					}
					else
					{
						found = found2;   /* A and B */
					}
				}
			}
		}

		if(found >= 0)
		{
			if(set_source == 0)
				set_source = k->source;
			if(set_category == 0)
				set_category = k->category;
			if(set_copy == 255)
				set_copy = k->copy_box;
			if(set_box == 255)
				set_box = k->box;
			if(set_colour == 0)
				set_colour = (k->status >> 4) & 0xf;
			if(set_status == 0)
				set_status = status_values[k->status & 7];

			score = cardex->score;

			switch(k->score_op)
			{
			case 1:
				score = k->score;
				break;
			case 2:
				score += k->score;
				break;
			case 3:
				score -= k->score;
				break;
			case 4:
				score *= k->score;
				break;
			case 5:
				if(k->score != 0)
					score /= k->score;
				break;
			}
			if(score > 9999)
				score = 9999;
			if(score < -9999)
				score = -9999;
			cardex->score = score;

			any_found = 1;
		}
	}

	if(any_found)
	{
		if(set_box != 255)
			cardex->docbox = set_box;

		if(cardex->score < options.filter_discard)
		{
			set_box = options.filter_score_box;
			cardex->docbox = set_box;
		}

		if(cardex->score < options.filter_set_read)
		{
			if(cardex->status <= STATUS_UNREAD)
				cardex->status = STATUS_READ;
		}

		if(set_colour != 0)
			cardex->colour = set_colour;

		if(set_copy > 0)
			cardex->copy_box = set_copy;

		if(set_source > 0)
			cardex->source = set_source;

		if(set_status > 0)
			cardex->status = set_status;

		if((set_category > 0) && (cardex->n_cats < (CD_N_CATS-1)))
		{
			cardex->cat_codes[cardex->n_cats++] = set_category;
		}

#ifdef deleted
		if(k_found->keywords > 0)
		{
			p = &k_found->string[k_found->keywords];
			keywords_add(cardex,p);
		}
#endif

	}

	if(set_box == 255)
		return(0);
	else
		return(1);
}   /* end of article_killfile */




void killfile_exit()
/******************/
{
	killfile_free_regex();
}   /* end of killfile_exit */





NEWSG *source_to_newsg(int source)
/*****************************/
{
	int  i;

	for(i=0; i<n_ngroups; i++)
	{
		if(ng_index[i]->source == source)
			return(ng_index[i]);
	}
	return(NULL);
}   /* end of source_to_newsg */





int fetch_full(FOLDREC *fr, CARD *cptr, int cancel)
/*************************************************/
{
	FILE *f;
	int  i;
	char *message_id;
	int  length;
	int  already;
	long  displ;
	long  displ2;
	char *server;
	NEWSG *ng;
	char fname[256];

	if((transport_opts & TOPS_NEWS_FETCH) == 0)
	{
		werr(0,"Fetching individual news articles is not supported by this Transport");
		return(-1);
	}


	if(((cptr->status & STATUS_BIT_HDR_ONLY) == 0) ||
			((cptr->status & STATUS_MASK2) == STATUS_DRAFT))
		return(1);

	message_id = &cptr->data[cptr->d_comment];
	if(message_id[0] != '<')
		return(2);


	switch(options.news_transport)
	{
	case X_VOYAGER:
	case X_TERMITE:
		return(-1);

	case X_NEWSHOUND:
		ng = source_to_newsg(cptr->source);
		if(ng == NULL)
			server = options.newshound_mnem;
		else
			server = ng->server;

		if(!isalnum(server[0]) || (strcmp_lc(server,"all")==0))
		{
			server = options.newshound_mnem;
		}

		sprintf(fname,"%s.%sGet",options.ngroups_data,server);
		break;

	case X_ANT:
		sprintf(fname,"%s./fetch",options.ngroups_data);
		break;
	}

	already = 0;
	f = fopen(fname,"r+");
	if(f != NULL)
	{
		/* check against message-ids already in the fetch list */
		length = strlen(message_id);
		while(!feof(f))
		{
			displ = ftell(f);
			if(fgets(fname,sizeof(fname),f)==NULL)
				break;

			if(memcmp(fname,message_id,length)==0)
			{
				/* this message id is already in the fetch list */
				if(cancel)
				{
					/* delete this line by replacing it with spaces */
					displ2 = ftell(f);
					fseek(f,displ,SEEK_SET);
					i = strlen(fname)-2;
					for(; i>=0; i--)
						fputc(' ',f);

					fseek(f,displ2,SEEK_SET);
				}
				else
				{
					already = 1;
				}
			}
		}
	}
	else
	{
		/* file doesn't already exist. create it */
		f = fopen(fname,"w");
		if(f==NULL)
		{
			werr(0,"Please re-select your Preferences->News->Transport");
			return(-1);
		}
	}

	if((cancel==0) && (already==0))
		fprintf(f,"%s\n",message_id);
	fclose(f);

	/* special status for 'requested full body' */
	if(cancel)
		cptr->status = (cptr->status & ~STATUS_MASK2) + STATUS_READ;
	else
		cptr->status = (cptr->status & ~STATUS_MASK2) + STATUS_FETCHING;
	article_update(fr,cptr,NULL,0,0);


	if(fetch_count != 3)
	{
		fetch_count = 3;
		save_box_any_read();
	}

	return(0);
}   /* end of fetch_full */



/******************************************************************/
/*        Address Book  -  permanent routines, not in overlay     */
/******************************************************************/


ADDR_BOOK **addr_list;
int n_addr_list;



int addr_list_lookup_name(char *name, int type, int ignore, int start)
/********************************************************************/
/* Type=0 name, 1=alias */
{
	int  ix;
	ADDR_BOOK *a;
	char buf[128];

	if((name == NULL) || (name[0] == 0))
		return(-1);

	strcpy_lc(buf,name);

	for(ix=start; ix<n_addr_list; ix++)
	{
		if(ix == ignore)
			continue;

		a = addr_list[ix];

		/* Match alias against alias */
		if(type == 0)
		{
			if(strcmp_lc(a->data,buf)==0)
				return(ix);
		}
		else
		{
			if(strcmp_lc(&a->data[a->alias],buf)==0)
				return(ix);
		}
	}
	return(-1);   /* not found */

}   /* end of addr_list_lookup_name */



int addr_list_lookup(char *name)
/******************************/
/* Look up address list entry from name.
   Check alias first, then name */
{
	int  ix;

	ix = addr_list_lookup_name(name,1,-1,0);
	if(ix >= 0)
		return(ix);

	return(addr_list_lookup_name(name,0,-1,0));
}   /* end of addr_list_lookup */





int addr_list_lookup_count(char *name, menu m, int *addr_ix)
/**********************************************************/
/* Count occurances of "name" and add to a menu if "m" is set */
{
	ADDR_BOOK *a;
	int  ix;
	int  i;
	int  count = 0;
	int  addr_ix2[128];

	if(addr_ix == NULL)
		addr_ix = addr_ix2;

	for(ix=0; ix>=0; ix++)
	{
		ix = addr_list_lookup_name(name,1,-1,ix);   /* lookup alias */
		if(ix >= 0)
		{
			a = addr_list[ix];

			if(m != NULL)
				menu_extend(m,&a->data[a->offset]);

			addr_ix[count++] = ix;
		}
		else
			break;
	}
	for(ix=0; ix>=0; ix++)
	{
		ix = addr_list_lookup_name(name,0,-1,ix);   /* lookup name */
		if(ix >= 0)
		{
			for(i=0; i<count; i++)
			{
				if(addr_ix[i]==ix)
					break;    /* already found this one, alias and name are the same */
			}
			if(i==count)
			{
				a = addr_list[ix];

				if(m != NULL)
					menu_extend(m,&a->data[a->offset]);

				addr_ix[count++] = ix;
			}
		}
		else
			break;
	}
	return(count);
}   /* end of addr_list_lookup_count */





int addr_list_lookup_menu(char *name)
/***********************************/
/* Lookup URL from name, show menu if there are ambiguities */
{
	menu m;
	int  hit;
	int  count=0;
	int  end_flag;
	wimp_mousestr mouse;
	wimp_eventstr e;
	int  addr_ix[128];


	m = menu_new(name,"");

	count = addr_list_lookup_count(name,m,addr_ix);

	if(count <= 1)
	{
		menu_dispose(&m,0);
		if(count==0)
			return(-1);

		return(addr_ix[0]);
	}

	event_clear_current_menu();
	wimp_get_point_info(&mouse);
	wimp_create_menu(menu_syshandle(m),mouse.x-40,mouse.y+20);

	end_flag = 0;
	while(!end_flag)
	{
		wimp_poll(event_getmask(),&e);
		switch(e.e)
		{
		case wimp_ENULL:
			break;  /* ignore */

		case wimp_EREDRAW:
		case wimp_EPTRLEAVE:
		case wimp_EPTRENTER:
		case wimp_ESEND:
		case wimp_ESENDWANTACK:
		case wimp_EACK:
			wimpt_fake_event(&e); /* stuff it back in the queue */
			event_process();
			break;

		case wimp_EMENU:
			hit = e.data.menu[0];
			end_flag = 1;
			break;

		default:
			wimpt_fake_event(&e); /* stuff it back in the queue */
			event_process();
			end_flag = 1;
			hit = -1;
			break;
		}
	}

	menu_dispose(&m,0);
	if(hit >= 0)
	{
		return(addr_ix[hit]);
	}
	return(-2);
}   /* end of addr_list_lookup_menu */




char *addr_lookup(char *name)
/***************************/
/* Look up URL from name */
{
	int  ix;
	ADDR_BOOK *a;

	ix = addr_list_lookup(name);

	if(ix == -1)
		return(NULL);

	a = addr_list[ix];
	return(&a->data[a->offset]);
}   /* end of addr_lookup */



ADDR_BOOK *addr_lookup_url2(char *url, int ignore)
/*******************************************************/
/* Lookup a URL in the address list */
{
	ADDR_BOOK *a;
	int  ix;

	if(url[0]==0)
		return(NULL);

	for(ix=0; ix<n_addr_list; ix++)
	{
		if(ix==ignore)
			continue;

		a = addr_list[ix];
		if(strcmp_lc(url,&a->data[a->offset])==0)
			return(a);
	}
	return(NULL);   /* not found */
}   /* end of addr_lookup_url2 */





ADDR_BOOK *addr_lookup_url(char *url)
/***********************************/
/* Lookup a URL in the address list */
{
	return(addr_lookup_url2(url,-1));   /* not found */
}   /* end of addr_lookup_url */





void addrlist_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y)
/****************************************************************************************/
{
	int  n_chars;
	ADDR_BOOK *a;
	char *name;
	char *url;
	char *alias;
	os_regset regs;
	char buf[32];

	static char addr_colr[] = {0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b};

	for(line=line; line<end_line; line++)
	{
		if(line >= b->n_entries)
			break;

		a = addr_list[line + folder_base];
		name = a->data;
		url = &a->data[a->offset];
		alias = &a->data[a->alias];

		list_set_colour(0, a->selected ^ 1,x,y);

		if(standard_font >= 0)
		{
			n_chars = strlen(name);


			paint_string(alias,x+48,y,244);
			paint_string(name,x+300,y,432);

			regs.r[0] = standard_font;
			regs.r[2] = 0x190;    /* bit 4, bit 7, bit 8 */
			regs.r[4] = y;

			regs.r[1] = (int)url;
			regs.r[3] = x+740;
			regs.r[7] = strlen(url);
			os_swi(0x40086+os_X,&regs);
		}
		else
		{
			bbc_move(x+48, y-4);
			strncpy(buf,alias,16);
			buf[16]=0;
			printf("%s",buf);

			bbc_move(x+300, y-4);
			strncpy(buf,name,28);
			buf[28]=0;
			printf("%s",buf);
			bbc_move(x+740, y-4);
			printf("%s",url);
		}

		display_icon(addr_colr[a->colr],4,y -2 - r->box.y1 + r->scy + status_sprite_offset,1);

		y -= lists_line_height;
	}
}   /* addrlist_display_window */




